﻿Imports System.Text
Imports IICBusinessLogic.PeriodProcessing
Imports IICBusinessLogic


<TestClass()>
Public Class TestDiplomaProcessor

#Region "Additional test attributes"

    Private testContextInstance As TestContext

    '''<summary>
    '''Gets or sets the test context which provides
    '''information about and functionality for the current test run.
    '''</summary>
    Public Property TestContext() As TestContext
        Get
            Return testContextInstance
        End Get
        Set(ByVal value As TestContext)
            testContextInstance = value
        End Set
    End Property
    '
    ' You can use the following additional attributes as you write your tests:
    '
    ' Use ClassInitialize to run code before running the first test in the class
    <ClassInitialize()> Public Shared Sub MyClassInitialize(ByVal testContext As TestContext)

        ModelContext.newForUnitTests()


    End Sub
    '
    ' Use ClassCleanup to run code after all tests in a class have run
    <ClassCleanup()> Public Shared Sub MyClassCleanup()
        ModelContext.release()
    End Sub


    'Use TestInitialize to run code before running each test
    '<TestInitialize()> _
    'Public Sub MyTestInitialize()
    'End Sub
    '
    ' Use TestCleanup to run code after each test has run
    <TestCleanup()> Public Sub MyTestCleanup()
    End Sub
    '
#End Region


    ''' <summary>
    ''' Test the case where a student has exception for a lesson 
    ''' that is a requirement for 2 certifications
    ''' </summary>
    ''' <remarks></remarks>
    <TestMethod()>
    Public Sub TestEarnExcpetionOnLessonWith2Certs()

        ModelContext.newForUnitTests()

        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()
            PeriodProcessorTestUtils.initialize()
            'create a diploma with 2 certificates
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithXCertReq(2)

            'create 2 lessons
            Dim lessons As List(Of Lesson) = New List(Of Lesson)
            For i As Integer = 1 To 2
                lessons.Add(PeriodProcessorTestUtils.CreateLesson(100, 70))
            Next
            LessonDataUtils.saveLesson(lessons.ToArray)

            'lesson 0 is a requirement in both cert 1 and 2
            diploma.DiplomaRequirements(0).Certification.AddLessonRequirement(lessons(0))

            diploma.DiplomaRequirements(1).Certification.AddLessonRequirement(lessons(0))
            diploma.DiplomaRequirements(1).Certification.AddLessonRequirement(lessons(1))

            DiplomaTypeDataUtils.saveDiplomaType(diploma)

            'create student and add exception to lesson(0)
            Dim studentcnt As Integer = _
                ModelContext.CurrentDBUtils.getIntValue("select max(StudentId) from Student")

            studentcnt = studentcnt + 1
            Dim stud As Student = StudentFactory.Create
            stud.FirstName = "t58 " & studentcnt
            stud.LastName = "t58 " & studentcnt
            stud.IdNumber = "t58" & studentcnt

            Dim studeException As StudentLessonCredit = StudentLessonCreditFactory.Create
            studeException.LessonId = lessons(0).LessonId
            studeException.EffectiveDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            stud.AddStudentLessonCredit(studeException)

            StudentDataUtils.saveStudent(stud)

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "59" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName
            PeriodDataUtils.savePeriod(period)

            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            'after period, the student must earn cert1 (he has exception)
            Assert.AreEqual(1, dc.certificatesEarned.Count)

            period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 2, maxPeriodDate)
            period.PeriodCode = "59.2" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")


            'create class to lesson 1,add student and give pass grade
            Dim pclass As PeriodClass = PeriodClassFactory.Create
            pclass.LessonId = lessons(1).LessonId
            pclass.ExamCenterID = 1
            pclass.MaxGrade = lessons(1).DefaultMaxGrade
            pclass.PassGrade = lessons(1).DefaultPassGrade

            period.AddClass(pclass)
            PeriodDataUtils.savePeriod(period)

            Dim studentManager As IStudentClassManager = New StudentClassManagerFactory().getStudentClassManager

            Dim cm As ClassMember = studentManager.addStudentToClass(stud, pclass)
            cm.Attend = True
            cm.Grade = pclass.PassGrade + 1

            pclass.AddMember(cm)
            period.AddClass(pclass)

            PeriodDataUtils.savePeriod(period)

            'now we see if student1 above has earned the diploma as expected
            dc = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            Assert.AreEqual(1, dc.diplomasEarned.Count)

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()
        Finally
            ModelContext.rollbackTrans()
        End Try
    End Sub


    ''' <summary>
    ''' Tests the case where a student earns a certificate w/o attending a class
    ''' but by having all exceptions
    ''' </summary>
    ''' <remarks></remarks>
    <TestMethod()>
    Public Sub TestEarnCertOnlyFromExceptions()

        Dim numberOfLessons As Integer = 2
        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()
            PeriodProcessorTestUtils.initialize()
            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithXCertReq(1)

            Dim lessons As List(Of Lesson) = New List(Of Lesson)

            Dim ldb As New LessonDBMapper()
            For i As Integer = 0 To 1
                lessons.Add(PeriodProcessorTestUtils.CreateLesson(100, 70))
            Next

            LessonDataUtils.saveLesson(lessons.ToArray)

            '***** add 2 lessons to certificate under diploma
            For Each dr As DiplomaRequirement In diploma.DiplomaRequirements
                For i As Integer = 0 To 1
                    dr.Certification.AddLessonRequirement(lessons(i))
                Next
            Next

            DiplomaTypeDataUtils.saveDiplomaType(diploma)

            'create student
            Dim studentcnt As Integer = _
                ModelContext.CurrentDBUtils.getIntValue("select max(StudentId) from Student")
            studentcnt = studentcnt + 1
            Dim stud As Student = StudentFactory.Create
            stud.FirstName = "t58 " & studentcnt
            stud.LastName = "t58 " & studentcnt
            stud.IdNumber = "t58" & studentcnt

            Dim studeException As StudentLessonCredit = StudentLessonCreditFactory.Create
            studeException.LessonId = lessons(0).LessonId
            studeException.EffectiveDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            stud.AddStudentLessonCredit(studeException)

            StudentDataUtils.saveStudent(stud)

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "59" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName
            pdb.save(period)


            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()
            'after processing, the studentException should not exist in ExceptionSatisfiedRequirement
            'todo: this was an old test assert before redesign, recheck
            'Assert.IsTrue(ExceptionSatisfiedRequirementDataUtils.findOne("StudentLessonExceptionId={0}", studeException.StudentLessonExceptionId) Is Nothing, "Not expecting exception in ExceptionSatisfiedRequirementData")

            'also, no diplomas or certificates are earned
            Assert.AreEqual(0, dc.diplomasEarned.Count)
            Assert.AreEqual(0, dc.certificatesEarned.Count)

            period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 2, maxPeriodDate)
            period.PeriodCode = "59.2" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")

            pdb.save(period)

            Dim studeException2 As StudentLessonCredit = StudentLessonCreditFactory.Create
            studeException2.LessonId = lessons(1).LessonId
            studeException2.EffectiveDate = period.PeriodDate
            stud.AddStudentLessonCredit(studeException2)
            StudentDataUtils.saveStudent(stud)
            Assert.AreEqual(2, stud.StudentLessonCredits.Count)

            'now we see if student1 above has earned the diploma as expected
            dc = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            Assert.AreEqual(1, dc.diplomasEarned.Count)

            'make sure that the exception was used to earn the diploma by the following 3 asserts
            Assert.IsNotNull(dc.diplomasEarned(0).DiplomaSatisfiedRequirements(0).certificationEarned.CertificationSatisfiedRequirements(0).StudentLessonExceptionUsageId)
            Assert.IsNotNull(dc.diplomasEarned(0).DiplomaSatisfiedRequirements(0).certificationEarned.CertificationSatisfiedRequirements(0).CreditUsage.StudentLessonExceptionId)
            Assert.AreEqual(dc.diplomasEarned(0).DiplomaSatisfiedRequirements(0).certificationEarned.CertificationSatisfiedRequirements(0).CreditUsage.StudentLessonExceptionId, studeException.StudentLessonExceptionId)

            Const STR_GET_ALL_EXCEPTION_REQS As String = _
                "CertificationRequirementId in (" & _
                " select CertificationRequirementId from CertificationRequirement where CertificationTypeId={0}) " & _
                " and [StudentLessonExceptionUsageId] in (SELECT a.StudentLessonExceptionUsageId FROM StudentLessonExceptionUsage AS a INNER JOIN " & _
                "StudentLessonException AS b ON a.StudentLessonExceptionId = b.StudentLessonExceptionId WHERE b.StudentId={1})"

            Dim dbm As New CertificationSatisfiedRequirementDBMapper()

            Dim certTypeid As Integer = diploma.DiplomaRequirements(0).CertificationTypeId.GetValueOrDefault
            Dim clst As List(Of CertificationSatisfiedRequirement) = dbm.findList(STR_GET_ALL_EXCEPTION_REQS, _
                            certTypeid, stud.StudentId)

            Assert.IsTrue(clst.Count = 2, "expected 2 satisifed requirements")
            For Each c As CertificationSatisfiedRequirement In clst

                Assert.IsNotNull(c.CertificationEarnedId, "Expected to have value in CertificationSatisfiedRequirement.CertificationEarnedId")
                Assert.IsNull(c.ClassMemberId, "Expected CertificationSatisfiedRequirement.ClassMemberId to be null ")
                Assert.IsNotNull(c.StudentLessonExceptionUsageId, "Expected CertificationSatisfiedRequirement.StudentLessonExceptionUsageId  to have value")


            Next

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()
        Finally
            ModelContext.rollbackTrans()
        End Try
    End Sub

    <TestMethod()>
    Public Sub TestPassFromClassAndExceptionMultiplePeriods()

        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson


        Try
            ModelContext.beginTrans()
            PeriodProcessorTestUtils.initialize()

            Dim multiplePeriodTest As New MultiplePeriodTest
            multiplePeriodTest.setupMultiplePeriodTest()

            ModelContext.rollbackTrans()
        Finally
            ModelContext.rollbackTrans()
        End Try


    End Sub

    ''' <summary>
    ''' Tests saving a PeriodClass. 
    ''' </summary>
    <TestMethod()>
    Public Sub TestDiplomaInSamePeriodWithException()

        ModelContext.newForUnitTests()

        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()

            PeriodProcessorTestUtils.initialize()

            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithOneCertReqAndXLessons(2)
            Dim cert1 As CertificationType = diploma.DiplomaRequirements(0).Certification

            Dim lessonReq As Lesson = LessonDataUtils.findByKey(cert1.CertificationRequirements(0).LessonId.GetValueOrDefault)
            Dim lessonReq2 As Lesson = LessonDataUtils.findByKey(cert1.CertificationRequirements(1).LessonId.GetValueOrDefault)

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName
            pdb.save(period)

            Dim studCnt As Integer = 0

            SyncLock Me
                studCnt = PeriodProcessorTestUtils.getNextStudentCounter
            End SyncLock
            'create 2 test students
            Dim student1 As Student = StudentFactory.Create
            student1.FirstName = "test " & studCnt
            student1.LastName = "srud " & studCnt
            student1.IdNumber = "T000" & studCnt

            Dim student2 As Student = StudentFactory.Create
            student2.FirstName = "test " & studCnt + 1
            student2.LastName = "srud " & studCnt + 1
            student2.IdNumber = "T000" & studCnt + 1
            StudentDataUtils.saveStudent(student1, student2)
            'Create 2 period classes and add both the new students as class members
            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lessonReq, period, student1, student2)

            'now save the PeriodClass.  This should save all objects to the database
            pdb.save(period)

            'confirm that student1 and student 2 have gotten new Ids from the insert statements
            Assert.IsTrue(student1.Id > 0)
            Assert.IsTrue(student2.Id > 0)
            Dim stud1id As Integer = student1.Id

            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lessonReq2, period, student1, student2)
            pdb.save(period)

            'set period to null and reload from database
            period = Nothing
            period = pdb.findWhere("PeriodName={0}", pername)
            Assert.IsTrue(Not period Is Nothing) 'confirm that the period was saved to the database
            Assert.AreEqual(2, period.Classes.Count) '2 classes in period were created

            Dim perClass1 As PeriodClass = period.getClass(0) ' get first class in the collection
            Dim lsn As Lesson = perClass1.Lesson
            Assert.AreEqual(lessonReq.LessonCode, lsn.LessonCode) 'expect to have the same lesson code as the one we setup above
            Assert.AreEqual(2, perClass1.Members.Count) 'we expect 2 members in the class

            Dim passGrade As Integer = CInt(perClass1.PassGrade)

            For i As Integer = 0 To perClass1.Members.Count - 1
                Dim member As ClassMember = perClass1.getMember(i)
                member.Attend = True
                If i = 0 Then
                    member.Grade = perClass1.PassGrade + 10 'one student passed
                Else
                    member.Grade = perClass1.PassGrade + 10 'fail this student
                End If
            Next

            Dim perClass2 As PeriodClass = period.getClass(1) ' get second class in the collection
            Assert.IsTrue(perClass2 IsNot Nothing)
            For i As Integer = 0 To perClass2.Members.Count - 1
                Dim member As ClassMember = perClass2.getMember(i)
                member.Attend = True
                If i = 0 Then
                    member.Grade = perClass2.PassGrade + 10 'one student passed
                Else
                    member.Grade = perClass2.PassGrade - 10 'fail this student
                End If
            Next

            'save again and re-load from database
            pdb.save(period) 'save again!
            period = Nothing
            period = pdb.findWhere("PeriodName={0}", pername)
            Assert.IsTrue(Not period Is Nothing)
            'check that the grades were saved to the database
            Assert.AreEqual(passGrade + 10, period.getClass(0).getMember(0).Grade)
            Assert.AreEqual(passGrade + 10, period.getClass(0).getMember(1).Grade)

            'now we see if student1 above has earned the requirement as expected
            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            'we will only have one certification earned since only student1 passed
            Assert.AreEqual(1, dc.certificatesEarned.Count)
            Assert.AreEqual(student1.StudentId, dc.certificatesEarned(0).StudentId)
            Assert.AreEqual(cert1.CertificationTypeId, dc.certificatesEarned(0).CertificationTypeId)

            'after we process the period, we should have no diplomas earned since
            'the diploma requires both certificates to be passed.
            Assert.AreEqual(1, dc.diplomasEarned.Count)

            student1 = Nothing
            student1 = New StudentDBMapper().findByKey(stud1id)

            Assert.AreEqual(1, student1.CertificationsEarned.Count())
            Assert.AreEqual(1, student1.DiplomasEarned.Count())

            'now test exceptions
            'certtification 1 above has lesson 1 and lessonReq2 as requirements
            'we add them to a new student and process the period. He/She should earn the 
            'certification
            Dim student3 As Student = StudentFactory.Create
            student3.FirstName = "test " & studCnt + 3
            student3.LastName = "srud " & studCnt + 3
            student3.IdNumber = "000" & studCnt + 3

            Dim sle As StudentLessonCredit = StudentLessonCreditFactory.Create
            sle.LessonId = lessonReq.LessonId
            sle.EffectiveDate = period.PeriodDate
            Dim sle2 As StudentLessonCredit = StudentLessonCreditFactory.Create
            sle2.LessonId = lessonReq2.LessonId
            sle2.EffectiveDate = period.PeriodDate

            student3.AddStudentLessonCredit(sle)
            student3.AddStudentLessonCredit(sle2)
            StudentDataUtils.saveStudent(student3)
            'now we see if student1 above has earned the requirement as expected
            dc = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            'we will only have one certification earned from student3
            Assert.AreEqual(1, dc.certificatesEarned.Count)

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()

        Finally
            ModelContext.rollbackTrans()
        End Try

    End Sub


    '''<summary>
    ''' Tests a diploma that has more than one certificate as a requirement
    ''' and tests passing parts of the diploma in different periods
    ''' </summary>
    ''' <remarks>
    '''both students pass lessons 1 and 3
    '''Student 1: pass lesson 2
    '''Student 2: fail lesson 2,
    '''
    '''------- EXPECTED OUTCOME: ------------ 
    '''Student1: earns 2 certificates, 2 dipoloma satisfied requirement
    '''Student2: earns 1 certificate, 1 diploma satisifed requirement
    ''' </remarks>
    <TestMethod()>
    Public Sub TestMultipleCertificateDiplomaInDifferentPeriods()

        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Dim diploma As DiplomaType

        Try
            ModelContext.beginTrans()
            PeriodProcessorTestUtils.initialize()

            'the diploma in this test needs both cert1 and cert2 to be earned
            'cert1: lesson 1 and 3
            'cert2: lesson 1 and 2
            diploma = PeriodProcessorTestUtils.createDiplomaWith2CertReq()
            Dim cert1 As CertificationType = diploma.DiplomaRequirements(0).Certification
            Dim cert2 As CertificationType = diploma.DiplomaRequirements(1).Certification

            Dim lesson1 As Lesson = LessonDataUtils.findByKey(cert1.CertificationRequirements(0).LessonId.GetValueOrDefault)
            Dim lesson2 As Lesson = LessonDataUtils.findByKey(cert1.CertificationRequirements(1).LessonId.GetValueOrDefault)
            Dim lesson3 As Lesson = LessonDataUtils.findByKey(cert2.CertificationRequirements(0).LessonId.GetValueOrDefault)

            'assign a value to the diploma we are working on
            diploma = cert1.DiplomaRequirements(0).Diploma

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName

            'create 2 test students
            Dim student1 As Student = StudentFactory.Create
            student1.FirstName = "test " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.LastName = "srud " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.IdNumber = "000" & PeriodProcessorTestUtils.getNextStudentCounter

            Dim student2 As Student = StudentFactory.Create
            student2.FirstName = "test " & PeriodProcessorTestUtils.getNextStudentCounter
            student2.LastName = "srud " & PeriodProcessorTestUtils.getNextStudentCounter
            student2.IdNumber = "000" & PeriodProcessorTestUtils.getNextStudentCounter
            StudentDataUtils.saveStudent(student1, student2)


            'now save the PeriodClass.  This should save all objects to the database
            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            pdb.save(period)

            'Create 3 period classes and add both the new students as class members
            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lesson1, period, student1, student2)
            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lesson3, period, student1, student2)
            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lesson2, period, student1, student2)

            pdb.save(period) 'save again!
            Assert.AreEqual(3, period.Classes.Count, "period.Classes.Count")
            Assert.AreEqual(2, period.Classes(0).Members.Count, "period.Classes(0).Members.Count")
            Assert.AreEqual(2, period.Classes(1).Members.Count, "period.Classes(0).Members.Count")
            Assert.AreEqual(2, period.Classes(2).Members.Count, "period.Classes(0).Members.Count")

            Dim perClass1 As PeriodClass = period.getClass(0) ' get first class in the collection
            Dim lsn As Lesson = perClass1.Lesson

            Dim passGrade As Integer = CInt(perClass1.PassGrade)

            'both students pass lessons 1 and 3
            'Student 1: pass lesson 2
            'Student 2: fail lesson 2,
            '
            '------- EXPECTED OUTCOME: ------------ 
            'Student1: earns 2 certificates, 2 dipoloma satisfied requirement
            'Student2: earns 1 certificate, 1 diploma satisifed requirement
            For i As Integer = 0 To perClass1.Members.Count - 1
                Dim member As ClassMember = perClass1.getMember(i)
                member.Attend = True
                member.Grade = perClass1.PassGrade + 10 'student passed
            Next

            Dim perClass2 As PeriodClass = period.getClass(1) ' get second class in the collection
            Assert.IsTrue(perClass2 IsNot Nothing)

            Dim lessonNotPassed As Lesson = perClass2.Lesson

            For i As Integer = 0 To perClass2.Members.Count - 1
                Dim member As ClassMember = perClass2.getMember(i)
                member.Attend = True
                If member.StudentId = student2.StudentId Then
                    member.Grade = perClass2.PassGrade - 10 'Student 2: fail lesson 2
                Else
                    member.Grade = perClass2.PassGrade + 10
                End If
            Next
            Dim perClass3 As PeriodClass = period.getClass(2)
            For i As Integer = 0 To perClass3.Members.Count - 1
                Dim member As ClassMember = perClass3.getMember(i)
                member.Attend = True
                member.Grade = perClass3.PassGrade + 10 'one student passed
            Next

            PeriodClassDataUtils.savePeriodClass(perClass1, perClass2, perClass3)

            period = Nothing
            period = pdb.findWhere("PeriodName={0}", pername)
            Assert.IsTrue(Not period Is Nothing)

            'now we see if student1 above has earned the requirement as expected
            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            'both students pass lessons 1 and 3
            'Student 1: pass lesson 2
            'Student 2: fail lesson 2, so student 1 earns 2 certificates, 
            'Student2: 1 certificate, 
            'total 3 crts earned
            Assert.AreEqual(3, dc.certificatesEarned.Count, "CertificatesEarned.Count")

            'Student 1 passed all lessons, so 1 diploma earned
            Assert.AreEqual(1, dc.diplomasEarned.Count, "DiplomasEarned.Count")
            Assert.AreEqual(2, student1.SatisifiedDiplomaRequirements(diploma.DiplomaTypeId).Count)
            Assert.AreEqual(1, student2.SatisifiedDiplomaRequirements(diploma.DiplomaTypeId).Count)
            Assert.AreEqual(6, dc.membersProcessedCount)

            'now create another period and pass student 2 in Lesson 2 so he/she can earn the 
            'second diploma
            period = Nothing
            period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 2, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            pdb.save(period)

            Dim cls As PeriodClass = PeriodClassFactory.Create
            cls.Lesson = lessonNotPassed
            cls.PeriodId = period.PeriodId
            cls.ExamCenterID = 1
            cls.MaxGrade = lessonNotPassed.DefaultMaxGrade
            cls.PassGrade = lessonNotPassed.DefaultPassGrade

            PeriodClassDataUtils.savePeriodClass(cls)
            Dim mn As IStudentClassManager = New StudentClassManagerFactory().getStudentClassManager
            mn.addStudentToClass(student2, cls)

            cls.Members(0).Grade = cls.PassGrade + 20
            PeriodClassDataUtils.savePeriodClass(cls)

            'now we see if student2 above has earned the requirement as expected
            dc = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            'we will only have one certification and 1 diploma earned from student2
            Assert.AreEqual(1, dc.certificatesEarned.Count)
            Assert.AreEqual(1, dc.diplomasEarned.Count)
            Assert.AreEqual(1, dc.membersProcessedCount)

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()

        Finally
            ModelContext.rollbackTrans()
        End Try

    End Sub

    ''' <summary>
    ''' Create a period, add class and classmember to the class.
    ''' Enter pass grade and process period. The student passes the class and 
    ''' therefore earns associated certification / diploma. If this student is 
    ''' added again in the next period in the same class and passes again, 
    ''' you should not get an error
    ''' </summary>
    ''' <remarks></remarks>
    <TestMethod()>
    Public Sub TestStudentEarnDiplomaTwice()

        Dim lessreq As Lesson
        Dim studentManager As IStudentClassManager = New StudentClassManagerFactory().getStudentClassManager

        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()

            PeriodProcessorTestUtils.initialize()

            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithOneCertReqOneLesson()
            Dim cert1 As CertificationType = diploma.DiplomaRequirements(0).Certification

            lessreq = LessonDataUtils.findByKey(cert1.CertificationRequirements(0).LessonId.GetValueOrDefault)

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            period.PeriodName = period.PeriodCode
            pdb.save(period)
            'create a test student
            Dim student As Student = StudentFactory.Create
            Dim studCnt As Integer = 0

            SyncLock Me
                studCnt = PeriodProcessorTestUtils.getNextStudentCounter
            End SyncLock

            student.FirstName = "2Dipl " & studCnt
            student.LastName = "2Dipl " & studCnt
            student.IdNumber = "2W0" & studCnt
            StudentDataUtils.saveStudent(student)

            'Create a period class and add the new student as class member
            Dim perClass As PeriodClass = PeriodClassFactory.Create
            perClass.LessonId = cert1.CertificationRequirements(0).LessonId
            perClass.ExamCenterID = 1
            perClass.PassGrade = perClass.Lesson.DefaultPassGrade
            perClass.MaxGrade = perClass.Lesson.DefaultMaxGrade

            period.AddClass(perClass)
            studentManager.addStudentToClass(student, perClass)

            pdb.save(period)

            period.Classes(0).Members(0).Grade = perClass.PassGrade + 10
            'now save the PeriodClass.  This should save all objects to the database
            pdb.save(period)

            'now we see if student1 above has earned the requirement as expected
            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()
            Assert.AreEqual(1, dc.membersProcessedCount)
            Assert.AreEqual(1, dc.certificatesEarned.Count)
            Assert.AreEqual(1, dc.diplomasEarned.Count)

            'create next Period of classes
            period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            period.PeriodName = period.PeriodCode
            pdb.save(period)

            'Create a period class and add the new student as class member
            perClass = PeriodClassFactory.Create
            perClass.Lesson = lessreq
            perClass.ExamCenterID = 1
            perClass.MaxGrade = lessreq.DefaultMaxGrade
            perClass.PassGrade = lessreq.DefaultPassGrade
            period.AddClass(perClass)
            pdb.save(period)
            studentManager.addStudentToClass(student, perClass)
            period.Classes(0).Members(0).Grade = perClass.PassGrade + 10
            'now save the PeriodClass.  This should save all objects to the database
            pdb.save(period)

            'now we see if student1 above has earned the requirement as expected
            Dim dc2 As PeriodProcessor = New PeriodProcessor(period)
            dc2.processPeriodAndStoreToDatabase()

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()
        Finally
            ModelContext.rollbackTrans()
        End Try

    End Sub


    '''<summary>
    ''' Tests a diploma that has just one certificate as a requirement
    ''' and a certificate with 1 lesson as a requirement
    ''' </summary>
    ''' <remarks>
    '''
    '''------- EXPECTED OUTCOME: ------------ 
    '''Student1: earns 1 certificate, 1 dipoloma satisfied requirement
    ''' </remarks>
    <TestMethod()>
    Public Sub TestSimpleProcessorDiplomaEarned()

        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()

            PeriodProcessorTestUtils.initialize()

            'the diploma in this test needs both cert1 and cert2 to be earned
            'cert1: lesson 1 and 3
            'cert2: lesson 1 and 2
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithOneCertReqAndXLessons(1)
            Dim cert1 As CertificationType = diploma.DiplomaRequirements(0).Certification
            Dim lesson1 As Lesson = LessonDataUtils.findByKey(cert1.CertificationRequirements(0).LessonId.GetValueOrDefault)

            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName

            'create a test students
            Dim student1 As Student = StudentFactory.Create
            student1.FirstName = "test " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.LastName = "srud " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.IdNumber = "000" & PeriodProcessorTestUtils.getNextStudentCounter
            StudentDataUtils.saveStudent(student1)


            'now save the PeriodClass.  This should save all objects to the database
            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            pdb.save(period)

            'Create a period class 
            PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers(lesson1, period, student1)


            pdb.save(period) 'save again!
            Assert.AreEqual(1, period.Classes.Count, "period.Classes.Count")
            Assert.AreEqual(1, period.Classes(0).Members.Count, "period.Classes(0).Members.Count")


            Dim perClass1 As PeriodClass = period.getClass(0) ' get first class in the collection
            Dim lsn As Lesson = perClass1.Lesson

            Dim passGrade As Integer = CInt(perClass1.PassGrade)

            'student passes lesson
            '------- EXPECTED OUTCOME: ------------ 
            'Student: earns 1 certificate, 1 dipoloma satisfied requirement
            Dim member As ClassMember = perClass1.getMember(0)
            member.Attend = True
            member.Grade = perClass1.PassGrade + 10 'student passed

            PeriodClassDataUtils.savePeriodClass(perClass1)

            'the value below will be used to check CertificationSatisfiedRequirement
            Dim checkClassMemberId = perClass1.getMember(0).Id

            period = Nothing
            period = pdb.findWhere("PeriodName={0}", pername)
            Assert.IsTrue(Not period Is Nothing)

            'now we see if student1 above has earned the requirement as expected
            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            Assert.AreEqual(1, dc.certificatesEarned.Count, "CertificatesEarned.Count")

            'Student 1 passed all lessons, so 1 diploma earned
            Assert.AreEqual(1, dc.diplomasEarned.Count, "DiplomasEarned.Count")
            Assert.AreEqual(1, student1.SatisifiedDiplomaRequirements(diploma.DiplomaTypeId).Count)
            Assert.AreEqual(1, dc.membersProcessedCount)

            'check data structure:
            Dim csr As CertificationSatisfiedRequirement = _
                    CertificationSatisfiedRequirementDataUtils.findOne("ClassMemberId={0}", checkClassMemberId)
            Assert.IsNotNull(csr, "Expected to find a CertificationSatisfiedRequirement")
            Assert.IsNotNull(csr.CertificationEarnedId, "Expected CertificationSatisfiedRequirement to be linked with CertificationEarned record")
            Assert.IsNotNull(csr.ClassMemberId, "Expected CertificationSatisfiedRequirement to be linked with ClassMemberId")
            Assert.AreEqual(csr.SatisfiedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)

            dc.diplomasEarned.ForEach(Sub(x)
                                          Assert.AreEqual(x.EarnedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)
                                      End Sub)

            dc.certificatesEarned.ForEach(Sub(x)
                                              Assert.AreEqual(x.EarnedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)
                                          End Sub)

            'check data structure:
            Dim dsr As DiplomaSatisfiedRequirement = _
                    DiplomaSatisfiedRequirementDataUtils.findOne("CertificationEarnedId={0}", csr.CertificationEarnedId)
            Assert.IsNotNull(dsr, "Expected to find a DiplomaSatisfiedRequirement")
            Assert.IsNotNull(dsr.DiplomaEarnedId, "Expected DiplomaSatisfiedRequirement to be linked with DiplomaEarned record")

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()

        Finally
            ModelContext.rollbackTrans()
        End Try

    End Sub


    '''<summary>
    ''' Tests a diploma that has just 1 certificate as a mandatory requirement,
    ''' 2 certificates as optional, with at least 1 required
    '''
    '''------- EXPECTED OUTCOME: ------------ 
    '''Student1: earns 1 certificate, 1 dipoloma satisfied requirement
    ''' </summary>
    <TestMethod()>
    Public Sub TestOptionalCertsDiplomaEarned()

        ModelContext.newForUnitTests()
        'diploma -> diploma requirement -> certification type -> certification requirement -> lesson
        Dim maxPeriodDate As Date = _
            ModelContext.CurrentDBUtils.getDtValue("select max(periodDate) from [Period]")

        Try
            ModelContext.beginTrans()

            PeriodProcessorTestUtils.initialize()

            Dim lessons As List(Of Lesson) = New List(Of Lesson)
            Dim requiredLesson As Lesson
            'the diploma in this test needs both cert1 and cert2 to be earned
            Dim diploma As DiplomaType = PeriodProcessorTestUtils.createDiplomaWithOneCertReqAndXLessons(0)
            Dim cert1 As CertificationType = diploma.DiplomaRequirements(0).Certification
            For i As Integer = 0 To 3
                Dim lessonCreated As Lesson = PeriodProcessorTestUtils.CreateLesson(100, 70)
                lessons.Add(lessonCreated)
                cert1.AddLessonRequirement(lessonCreated)

                'there is one Lesson as mandatory at position 0, so we do i+1
                cert1.CertificationRequirements(i).IsOptional = i > 0
                If i = 0 Then
                    requiredLesson = lessonCreated
                End If
            Next
            LessonDataUtils.saveLesson(lessons.ToArray)

            cert1.NumberOptionalRequired = 1
            CertificationTypeDataUtils.saveCertificationType(cert1)


            'create a Period of classes
            Dim period As Period = PeriodFactory.Create
            period.DiplomaSuperTypeId = PeriodProcessorTestUtils.diplomaSuperType.DiplomaSuperTypeId
            period.PeriodDate = DateAdd(DateInterval.Month, 1, maxPeriodDate)
            period.PeriodCode = "TS" & ModelContext.CurrentDBUtils.getIntValue("select count(*)+1 from period")
            Dim pername As String = period.PeriodName

            'create a test students
            Dim student1 As Student = StudentFactory.Create
            student1.FirstName = "test " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.LastName = "stud " & PeriodProcessorTestUtils.getNextStudentCounter
            student1.IdNumber = "000" & PeriodProcessorTestUtils.getNextStudentCounter
            StudentDataUtils.saveStudent(student1)

            'now save the PeriodClass.  This should save all objects to the database
            Dim pdb As PeriodDBMapper = New PeriodDBMapper
            pdb.save(period)

            'Create a period classes with one mandatory, 1 optional, 
            'student passes lesson 1 required lesson + 1 optional
            For i As Integer = 0 To 1
                Dim perClass1 As PeriodClass = PeriodProcessorTestUtils.CreateClassAndAddStudentsAsClassMembers( _
                                    lessons(i), period, student1)

                Dim member As ClassMember = perClass1.getMember(0)
                member.Attend = True
                member.Grade = perClass1.PassGrade + 10 'student passed
            Next

            Assert.IsTrue(period.Classes(0).Lesson.CertificationRequirements(0).IsMandatory = True)
            Assert.IsTrue(period.Classes(1).Lesson.CertificationRequirements(0).IsMandatory = False)


            pdb.save(period) 'save again!

            'the value below will be used to check CertificationSatisfiedRequirement
            Dim checkClassMemberId = period.Classes(0).getMember(0).Id

            period = Nothing
            period = pdb.findWhere("PeriodName={0}", pername)
            Assert.IsTrue(Not period Is Nothing)

            'now we see if student1 above has earned the requirement as expected
            Dim dc As PeriodProcessor = New PeriodProcessor(period)
            dc.processPeriodAndStoreToDatabase()

            Assert.AreEqual(1, dc.certificatesEarned.Count, "CertificatesEarned.Count")

            'Student 1 passed 1 req lesson, 1 optional, so 1 diploma earned
            Assert.AreEqual(1, dc.diplomasEarned.Count, "DiplomasEarned.Count")
            Assert.AreEqual(1, student1.SatisifiedDiplomaRequirements(diploma.DiplomaTypeId).Count)

            'check data structure:
            Dim csr As CertificationSatisfiedRequirement = _
                    CertificationSatisfiedRequirementDataUtils.findOne("ClassMemberId={0}", checkClassMemberId)
            Assert.IsNotNull(csr, "Expected to find a CertificationSatisfiedRequirement")
            Assert.IsNotNull(csr.CertificationEarnedId, "Expected CertificationSatisfiedRequirement to be linked with CertificationEarned record")
            Assert.IsNotNull(csr.ClassMemberId, "Expected CertificationSatisfiedRequirement to be linked with ClassMemberId")
            Assert.AreEqual(csr.SatisfiedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)

            dc.diplomasEarned.ForEach(Sub(x)
                                          Assert.AreEqual(x.EarnedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)
                                      End Sub)

            dc.certificatesEarned.ForEach(Sub(x)
                                              Assert.AreEqual(x.EarnedPeriodProcessId, dc.PeriodProcess.PeriodProcessId)
                                          End Sub)

            'check data structure:
            Dim dsr As DiplomaSatisfiedRequirement = _
                    DiplomaSatisfiedRequirementDataUtils.findOne("CertificationEarnedId={0}", csr.CertificationEarnedId)
            Assert.IsNotNull(dsr, "Expected to find a DiplomaSatisfiedRequirement")
            Assert.IsNotNull(dsr.DiplomaEarnedId, "Expected DiplomaSatisfiedRequirement to be linked with DiplomaEarned record")

            'rollback so we do not save to the database
            ModelContext.rollbackTrans()

        Finally
            ModelContext.rollbackTrans()
        End Try

    End Sub
End Class
